#include "sinmain.h"
#include "ui_sinmain.h"
#include <QRgb>
#include <QFile>
#include <QMessageBox>
#include <QTextStream>
#include <QFileDialog>
#include <qDebug>

Sinmain::Sinmain(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Sinmain)
{
    ui->setupUi(this);
}

Sinmain::~Sinmain()
{
    delete ui;
}

float Sinmain::convertToGrayscale(int r,int g,int b)
{
    return (r * 0.2989) + (g * 0.5870) + (b * 0.1140);
}
float Sinmain::normalizeValue(float val,float from,float to)
{
    return (val*to/from);
}
void  Sinmain::getImageHeights(QImage src)
{
    global_pic  = src;
    QRgb *pixels = (QRgb *)src.scanLine(0);
    for(int i=0;i<src.height();i++)
    {
        for(int j=0;j<src.width();j++)
        {
//            qDebug()<<pixels[i*src.width()+j];
            QRgb pp = pixels[i*src.width()+j];
            float re = convertToGrayscale(qRed(pp),qGreen(pp),qBlue(pp));
            float po = normalizeValue(re,255,10);
            map_pix.append(po);
        }
    }
}

//generatr the 3d cube box for every pixcel
void Sinmain::getModelAreas()
{
    float border = 1.0;
    float base = 0.4;      // means the model will always have a 0.4mm base
    float scale = 0.2 ;    // each of the 10 layers will be 0.2 mm in height
    float zenith = base + (scale*10);
    float maxWidth = scale*global_pic.width();
    float maxHeight = scale*global_pic.height();
    for(int h = 0;h<global_pic.height();h++)
    {
        for(int w=0;w<global_pic.width();w++)
        {
            float x0 = w*scale;
            float x1 = x0 + scale;
            float y0 = h*scale;
            float y1 = y0 + scale;
            float z0 = 0;
            float z1 = base + (scale*(map_pix[h*global_pic.width()+w]));

            int cur_w = global_pic.width();
            int cur_h = global_pic.height();
            //back face
            face back_f;
            back_f.faces.append(QVector3D(x0,y0,z0));
            back_f.faces.append(QVector3D(x1,y0,z0));
            back_f.faces.append(QVector3D(x1,y1,z0));
            back_f.faces.append(QVector3D(x0,y1,z0));
            cubes.append(back_f);
            //front face
            face front_f;
            front_f.faces.append(QVector3D(x0,y0,z1));
            front_f.faces.append(QVector3D(x0,y1,z1));
            front_f.faces.append(QVector3D(x1,y1,z1));
            front_f.faces.append(QVector3D(x1,y0,z1));
            cubes.append(front_f);

           // top border wall
            if(h==0)
            {
                face top_f;
                top_f.faces.append(QVector3D(x1,y0,z0));
                top_f.faces.append(QVector3D(x0,y0,z0));
                if(w>0)
                {
                    if(map_pix[h*cur_w+w]>map_pix[(h)*cur_w+w-1])
                    {
                        top_f.faces.append(QVector3D(x0,y0,(scale*map_pix[(h)*cur_w+w-1])));
                    }
                }
                top_f.faces.append(QVector3D(x0,y0,z1));
                top_f.faces.append(QVector3D(x1,y0,z1));

                if(w<(cur_w-1))
                {
                    if(map_pix[h*cur_w+w]>map_pix[(h)*cur_w+w+1])
                    {
                        top_f.faces.append(QVector3D(x1,y0,scale*(map_pix[(h)*cur_w+w+1])));
                    }
                }
                cubes.append(top_f);
            }

            //left border wall
            if(w==0)
            {
                face left_f;
                left_f.faces.append(QVector3D(x0,y1,z1));
                left_f.faces.append(QVector3D(x0,y0,z1));
                if(h>0)
                {
                    if(map_pix[h*cur_w+w]>map_pix[(h-1)*cur_w+w])
                    {
                        left_f.faces.append(QVector3D(x0,y0,scale*(map_pix[(h-1)*cur_w+w])));
                    }
                }
                left_f.faces.append(QVector3D(x0,y0,z0));
                left_f.faces.append(QVector3D(x0,y1,z0));
                if(h<(cur_h-1))
                {
                    if(map_pix[w*cur_w+h]>map_pix[(h+1)*cur_w+w])
                    {
                        left_f.faces.append(QVector3D(x0,y1,scale*(map_pix[(h+1)*cur_w+w])));
                    }
                }
                cubes.append(left_f);
            }

            //bottom face of each pixcel
            if(h==(cur_h-1))
            {
                face bottom_f;
                bottom_f.faces.append(QVector3D(x1,y1,z1));
                bottom_f.faces.append(QVector3D(x0,y1,z1));
                if(w>0)
                {
                    if(map_pix[h*cur_w+w]>map_pix[(h)*cur_w+w-1])
                    {
                        bottom_f.faces.append(QVector3D(x0,y1,scale*(map_pix[(h)*cur_w+w-1])));
                    }
                }
                bottom_f.faces.append(QVector3D(x0,y1,z0));
                bottom_f.faces.append(QVector3D(x1,y1,z0));
                if(w<(cur_w-1))
                {
                    if(map_pix[h*cur_w+w]>map_pix[(h)*cur_w+w+1])
                    {
                        bottom_f.faces.append(QVector3D(x1,y1,scale*(map_pix[(h)*cur_w+w+1])));
                    }
                }
                cubes.append(bottom_f);
            }
            else
            {
                if(map_pix[h*cur_w+w]!=map_pix[(h+1)*cur_w+w])
                {
                    float z2 = base+scale*(map_pix[(h+1)*cur_w+w]);
                    face bot2_f;
                    bot2_f.faces.append(QVector3D(x1, y1, z1));
                    bot2_f.faces.append(QVector3D(x0, y1, z1));
                    bot2_f.faces.append(QVector3D(x1, y1, z2));
                    bot2_f.faces.append(QVector3D(x1, y1, z2));
                    cubes.append(bot2_f);
                }
            }

            //right face of each pixcel
            if(w==(cur_w-1))
            {
                face right_f;
                right_f.faces.append(QVector3D(x1,y1,z0));
                right_f.faces.append(QVector3D(x1,y0,z0));
                if(h>0)
                {
                    if(map_pix[h*cur_w+w]>map_pix[(h-1)*cur_w+w])
                    {
                        right_f.faces.append(QVector3D(x1,y1,scale*(map_pix[(h-1)*cur_w+w-1])));
                    }
                }
                right_f.faces.append(QVector3D(x1,y0,z1));
                right_f.faces.append(QVector3D(x1,y1,z1));

                if(h<(cur_h-1))
                {
                    if(map_pix[h*cur_w+w]>map_pix[(h+1)*cur_w+w])
                    {
                        right_f.faces.append(QVector3D(x1,y1,scale*(map_pix[(h+1)*cur_w+w])));
                    }
                }
                cubes.append(right_f);
            }
            else
            {
                if(map_pix[h*cur_w+w]!=map_pix[(h)*cur_w+w+1])
                {
                    float z2 = base+scale*(map_pix[(h)*cur_w+w+1]);
                    face right2_f;
                    right2_f.faces.append(QVector3D(x1, y0, z1));
                    right2_f.faces.append(QVector3D(x1, y1, z1));
                    right2_f.faces.append(QVector3D(x1, y1, z2));
                    right2_f.faces.append(QVector3D(x1, y0, z2));
                    cubes.append(right2_f);

                }
            }

        }
    }
}

void Sinmain::areasToTriangles()
{
    int routes[7][4][3]={
        {{0},{0},{0},{0}},
        {{0},{0},{0},{0}},
        {{0},{0},{0},{0}},
        {{0,1,2},{0},{0},{0}},
        {{0,1,2},{0,2,3},{0},{0}},
        {{0,1,4},{1,2,4},{2,3,4},{0}},
        {{0,1,2},{2,3,5},{3,4,5},{5,0,2}}
    };
    //根据一个面中点的数量来决定生成三角面的数量
    //3 point --->  1 face
    //4 point --->  2 face
    for(int i=0;i<cubes.count();i++)
    {
        int l = cubes.at(i).faces.count();
        if((l>=3)&&(l<=6))
        {
            int j = l-2;
            for(int num=0;num<j;num++)
            {
                triangles tri;
                tri.triangle.append(cubes.at(i).faces.at(routes[l][num][0]));
                tri.triangle.append(cubes.at(i).faces.at(routes[l][num][1]));
                tri.triangle.append(cubes.at(i).faces.at(routes[l][num][2]));
                triangle_out.push_back(tri);
            }
        }
    }
}
void Sinmain::createASCIISTL()
{
    QFile file("out.stl");
    //方式：Append为追加，WriteOnly，ReadOnly
    if (!file.open(QIODevice::WriteOnly|QIODevice::Text)) {
        QMessageBox::critical(NULL, "提示", "无法创建文件");
        return ;
    }
    QTextStream out(&file);
  //  out<<"hello \n";
    for(int i=0;i<triangle_out.count();i++)
    {
        QString tri_f;
        triangles tri_temp = triangle_out.at(i);
        tri_f="  facet normal 0.0 0.0 0.0\n";
        tri_f+= "    outer loop\n";
        tri_f+=tr("      vertex  %1  %2  %3 \n").arg(tri_temp.triangle.at(0).x()).arg(tri_temp.triangle.at(0).y()).arg(tri_temp.triangle.at(0).z());
        tri_f+=tr("      vertex  %1  %2  %3 \n").arg(tri_temp.triangle.at(1).x()).arg(tri_temp.triangle.at(1).y()).arg(tri_temp.triangle.at(1).z());
        tri_f+=tr("      vertex  %1  %2  %3 \n").arg(tri_temp.triangle.at(2).x()).arg(tri_temp.triangle.at(2).y()).arg(tri_temp.triangle.at(2).z());
        tri_f+="    endloop\n";
        tri_f+="  endfacet\n";
        out<<tri_f;
    }
    out<<"endsolid";
    out.flush();
    file.close();
}

// open picture
void Sinmain::on_openPic_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,tr("打开图片文件"),"./",tr("*.png *.bmp *.jpg"));
    QImage pic(fileName);
    getImageHeights(pic);
}

//generate cube for every pixcels
void Sinmain::on_generateCube_clicked()
{
    getModelAreas();
}

//export triangles to STL file
void Sinmain::on_exportSTL_clicked()
{
    areasToTriangles();
    createASCIISTL();
}
